package se.webbzon.boltzmann.game.object.dialogue;

import java.util.ArrayList;
import java.util.List;

import se.webbzon.boltzmann.game.object.dialogue.layout.ChoiceDialogueLayout;
import se.webbzon.boltzmann.game.object.dialogue.layout.DialogueLayout;
import se.webbzon.boltzmann.game.object.dialogue.word.AbstractDialogueWord;
import se.webbzon.boltzmann.input.Button;
import se.webbzon.boltzmann.input.Input;
import se.webbzon.oschi01.worldarea.WorldArea;
import se.webbzon.oschi01.worldobject.appearance.GroupAppearance3D;

public class ChoiceDialogue extends AbstractDialogue {
	
	// The choices in this dialogue
	private final List<AbstractDialogueWord> choices;
	
	// The continue circle
	private final DialogueCircle circle;
	
	// The choice
	private volatile int choice;
	
	// Calls all the listeners in the next step if set
	private volatile boolean proceed;
	
	// Shows all the words in the next step if set
	private volatile boolean showAll;

	/*============================================================
	Constructors
	============================================================*/
	
	/** Creates a new multiple choice dialogue. The words and choices 
	 * in the dialogue will be parsed from the given text. **/
	public ChoiceDialogue(	ChoiceDialogueLayout layout,
							DialogueBackground background,
							String text, 
							List<String> choices) {
		this(	layout, background,
				DialogueTextParser.parse(layout,text),
				parseChoices(layout,choices));
	}
	
	/** Creates a new multiple choice dialogue with a given
	 * list of words and choices. **/
	public ChoiceDialogue(	ChoiceDialogueLayout layout,
							DialogueBackground background,
							List<AbstractDialogueWord> words,
							List<AbstractDialogueWord> choices) {
		super(layout,background,words);
		
		this.choices = choices;
		proceed = false;
		showAll = false;
		choice = 0;
		
		/*GroupAppearance3D groupAppearance = 
				((GroupAppearance3D) getAppearance());*/
		
		/*final double circleRadius = layout.getCircleRadius();
		final double choiceX = circleRadius + layout.getChoiceXSpacing();
		if (choices.size() <= 2) {
			// Less than two choices
			final double xOffset = - layout.getWidth()/2 + choiceX;
			double yOffset = 0;
			for (AbstractDialogueWord choice : choices) {
				choice.setPosition(xOffset, yOffset, 0);
				yOffset -= 	layout.getCharacterHeight() + layout.getSpacing();
				groupAppearance.addAppearance(choice.getAppearance()); 
			}
		} else {
			// More than two choices
			boolean left = true;
			double yOffset = 0;
			for (AbstractDialogueWord choice : choices) {
				final double xOffset = left ? - layout.getWidth()/2 + choiceX : choiceX;
				choice.setPosition(xOffset, yOffset, 0);
				if (!left)
					yOffset -= 	layout.getCharacterHeight() + layout.getSpacing();
				left = !left;
				groupAppearance.addAppearance(choice.getAppearance()); 
			}
		}*/
		
		updateChoicePositions();
		circle = new DialogueCircle(layout.getCircleRadius());
		circle.hide();
		getAppearance().addAppearance(circle.getAppearance());
		updateCirclePosition();
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** Returns the selected choice of the choice dialogue. **/
	public String getChoice() {
		return choices.get(choice).getWord();
	}
	
	public int getSelection() {
		return choice;
	}
	
	@Override public GroupAppearance3D getAppearance() {
		return (GroupAppearance3D) super.getAppearance();
	}
	
	@Override public ChoiceDialogueLayout getLayout() {
		return (ChoiceDialogueLayout) super.getLayout();
	}

	@Override public void hide() {
		super.hide();
		proceed = false;
		showAll = false;
	}

	@Override public void onShowInput() {
		choice = 0;
		updateCirclePosition();
		circle.display();
		for (AbstractDialogueWord choice : choices)
			choice.showAllCharacters(this);
	}
	
	@Override public void onHideInput() {
		circle.hide();
		for (AbstractDialogueWord choice : choices)
			choice.setCharacter(this, 0);
	}
	
	@Override public void onStep(WorldArea world) {
		super.onStep(world);
		if (proceed) {
			proceed = false;
			notifyOnSelect();
		} else if (showAll) {
			showAll = false;
			showAllWords();
		}
	}

	@Override public void primaryAxisChanged(Input input, double direction, double value) {};

	@Override public void secondaryAxisChanged(Input input, double direction, double value) {};

	@Override public void buttonPressed(Input input, Button button) {
		if (isInputReady()) {
			final int currentChoice = choice;
			switch (button) {
			case ACTION:
				if (isAllVisible())
					proceed = true;
				else
					showAll = true;
				break;
			case DOWN:
				if (choice == choices.size()-2)
					choice = choices.size()-1;
				else if (choice == choices.size()-1)
					choice = 0;
				else
					choice += 2;
				break;
			case RIGHT:
				if (choice == choices.size()-1)
					choice = 0;
				else
					choice++;
				break;
			case UP:
				if (choice == 1)
					choice = 0;
				else if (choice == 0)
					choice = choices.size()-1;
				else
					choice -= 2;
				break;
			case LEFT:
				if (choice == 0)
					choice = choices.size()-1;
				else
					choice--;
				break;
			}
			if (currentChoice != choice)
				updateCirclePosition();
		}
	}

	@Override public void buttonReleased(Input input, Button button) {};
	
	@Override public void bindingAccepted(Input input, Button button) {};
	
	/*============================================================
	Private Methods
	============================================================*/
	
	/** Notifies all listeners that a selection has been made. **/
	private void notifyOnSelect() {
		for (DialogueListener listener : getListeners().toArray(new DialogueListener[0])) {
			if (listener instanceof ChoiceDialogueListener)
				((ChoiceDialogueListener) listener).onSelect(this);
		}
	}
	
	/** Updates the position of the choices. **/
	private final void updateChoicePositions() {
		ChoiceDialogueLayout layout = getLayout();
		GroupAppearance3D appearance = getAppearance();
		final double choiceX = layout.getCircleRadius() + layout.getChoiceXSpacing();
		final boolean ltThree = choices.size() < 3;
		boolean left = true;
		double yOffset = 0, xOffset;
		for (AbstractDialogueWord choice : choices) {
			if (left || ltThree)
				xOffset = - layout.getWidth()/2 + choiceX;
			else
				xOffset = choiceX;
			
			// Update the position of the choice
			choice.setPosition(xOffset, yOffset, 0);
			appearance.addAppearance(choice.getAppearance());
			
			if (!left || ltThree)
				yOffset -= 	layout.getCharacterHeight() + layout.getSpacing();
			if (!ltThree)
				left = !left;
		}
	}
	
	/** Updates the position of the continue circle. **/
	private void updateCirclePosition() {
		final ChoiceDialogueLayout layout = getLayout();
		final double xo, yo;
		if (choices.size() < 3) {
			// Less than three choices
			xo = - getLayout().getWidth()/2;
			yo = - (layout.getCharacterHeight() + layout.getSpacing()) * 
					((double) choice) + 0.5 * layout.getCircleRadius();
		} else {
			// More than two choices
			xo = (choice % 2 == 0) ? - getLayout().getWidth()/2 : 0;
			yo = - (layout.getCharacterHeight() + layout.getSpacing()) * 
					((double) (choice / 2)) + 0.5 * layout.getCircleRadius();
		}
		circle.setPosition(	xo + layout.getCircleRadius(), 
							yo - layout.getCharacterHeight(), 0);
	}
	
	/** Parses a list of strings to a list of dialogue words. **/
	private static final List<AbstractDialogueWord> parseChoices(	DialogueLayout layout,
															List<String> choices) {
		ArrayList<AbstractDialogueWord> words = new ArrayList<AbstractDialogueWord>();
		for (String choice : choices) {
			List<AbstractDialogueWord> result = DialogueTextParser.parse(layout,choice);
			for (AbstractDialogueWord word : result) {
				words.add(word);
				break;
			}
		}
		return words;
	}
	
}
